package com.ai.service.made.impl;

import cn.hutool.core.util.ObjectUtil;
import com.ai.common.CommonResponse;
import com.ai.common.constant.Constants;
import com.ai.common.constant.ModbusConstants;
import com.ai.common.constant.OpcConstant;
import com.ai.common.constant.RedisConstants;
import com.ai.entity.made.ModbusNodeInfo;
import com.ai.entity.made.OpcNodeInfo;
import com.ai.entity.made.OpcServerInfo;
import com.ai.entity.made.page.OpcNodeInfoPage;
import com.ai.mapper.made.OpcNodeInfoMapper;
import com.ai.model.dto.OpcNodeInfoDto;
import com.ai.model.vo.DeviceInfoVo;
import com.ai.opc.DynamicOpcNodeSubscriber;
import com.ai.opc.OpcNodeRunnable;
import com.ai.opc.OpcThreadPoolExecutor;
import com.ai.service.made.CameraService;
import com.ai.service.made.IModbusNodeInfoService;
import com.ai.service.made.IOpcNodeInfoService;
import com.ai.service.made.IOpcServerInfoService;
import com.ai.util.SecurityUtils;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import io.swagger.models.auth.In;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.milo.opcua.sdk.client.OpcUaClient;
import org.eclipse.milo.opcua.sdk.client.api.UaClient;
import org.eclipse.milo.opcua.sdk.client.api.subscriptions.UaSubscription;
import org.eclipse.milo.opcua.stack.core.types.builtin.NodeId;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author jiaok
 * @since 2024-11-14
 */
@Service
@Slf4j
public class OpcNodeInfoServiceImpl extends ServiceImpl<OpcNodeInfoMapper, OpcNodeInfo> implements IOpcNodeInfoService {

    @Autowired
    private IOpcServerInfoService iOpcServerInfoService;


    @Resource
    private IModbusNodeInfoService iModbusNodeInfoService;

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    @Resource
    private CameraService cameraService;

    @Value("${opc.threshold}")
    private  Integer threshold ;

    @Override
    public CommonResponse<String> add(OpcNodeInfoDto opcNodeInfoDto) {

        OpcNodeInfo opcNodeInfo = new OpcNodeInfo();
        BeanUtils.copyProperties(opcNodeInfoDto,opcNodeInfo);
        opcNodeInfo.setState("0");
        opcNodeInfo.setCreateUser(SecurityUtils.getLoginUserName());
        opcNodeInfo.setCreateTime(LocalDateTime.now());
        opcNodeInfo.setUpdateUser(SecurityUtils.getLoginUserName());
        opcNodeInfo.setUpdateTime(LocalDateTime.now());
        // TODO: 2024-11-15 前端回传，这里暂先写一下
        opcNodeInfo.setNamespace(2);
        this.save(opcNodeInfo);


        //更新
        iOpcServerInfoService.update(new UpdateWrapper<OpcServerInfo>()
                .eq("id",opcNodeInfoDto.getOpcServerInfoId())
                .setSql("communication_node_num = communication_node_num + 1"));

        OpcThreadPoolExecutor.executorService.submit(new OpcNodeRunnable(opcNodeInfo,iOpcServerInfoService,this));

        return CommonResponse.ok("节点新增成功");
    }

    @Override
    public void handleNodeValue(NodeId nodeId, String value) {

        String nodeIdentifier = nodeId.getIdentifier().toString();
        //获取当前标识符对应的节点的id
        String nodeID = stringRedisTemplate.opsForValue().get(RedisConstants.OPC_NODE_NODE_INFO + nodeIdentifier);
        //判断当前节点的阈值是否大于指定----默认为5
//        stringRedisTemplate.opsForValue().set(RedisConstants.OPC_NODE_IDENTIFIER+nodeIdentifier,Double.parseDouble(value)>threshold?"1":"0");
        //统一缓存中的设备信号键
        stringRedisTemplate.opsForValue().set(RedisConstants.DEVICE_SIGNAL+nodeID,Double.parseDouble(value)>threshold?"1":"0");
    }

    @Override
    public CommonResponse getOpcNodeNameList(String deviceServerId,String deviceType) {

        ArrayList<DeviceInfoVo> deviceInfoVos = new ArrayList<>();

        if (OpcConstant.DEVICE_TYPE.equals(deviceType)){
            //处理opc节点
            List<OpcNodeInfo> opcNodeInfoList = list(new QueryWrapper<OpcNodeInfo>()
                    .select("id", "communication_node_name").eq("opc_server_info_id", deviceServerId));
            if (!CollectionUtils.isEmpty(opcNodeInfoList)){
                List<DeviceInfoVo> opcDeviceInfoVoList = opcNodeInfoList.stream().map(opcNodeInfo -> {
                            DeviceInfoVo deviceInfoVo = new DeviceInfoVo();
                            deviceInfoVo.setDeviceNodeName(opcNodeInfo.getCommunicationNodeName());
                            deviceInfoVo.setDeviceNodeId(opcNodeInfo.getId());
                            deviceInfoVo.setDeviceType(OpcConstant.DEVICE_TYPE);
                            return deviceInfoVo;
                        }).sorted(Comparator.comparing(DeviceInfoVo::getDeviceNodeName))
                        .collect(Collectors.toList());
                deviceInfoVos.addAll(opcDeviceInfoVoList);
            }
        } else if (ModbusConstants.DEVICE_TYPE.equals(deviceType)) {
            //处理modbus节点
            List<ModbusNodeInfo> modbusNodeInfoList = iModbusNodeInfoService.list(new QueryWrapper<ModbusNodeInfo>().eq("modbus_server_id", deviceServerId));
            if (!CollectionUtils.isEmpty(modbusNodeInfoList)){
                List<DeviceInfoVo> opcDeviceInfoVoList = modbusNodeInfoList.stream().map(modbusNodeInfo ->  {
                            DeviceInfoVo deviceInfoVo = new DeviceInfoVo();
                            deviceInfoVo.setDeviceNodeName(modbusNodeInfo.getNodeName());
                            deviceInfoVo.setDeviceNodeId(modbusNodeInfo.getId());
                            deviceInfoVo.setDeviceType(ModbusConstants.DEVICE_TYPE);
                            return deviceInfoVo;
                        }).sorted(Comparator.comparing(DeviceInfoVo::getDeviceNodeName))
                        .collect(Collectors.toList());
                deviceInfoVos.addAll(opcDeviceInfoVoList);
            }
        } else if (Constants.DEVICE_TYPE_SIGNAL_CAMERA.equals(deviceType)) {
            //处理信号摄像头
            deviceInfoVos = cameraService.getSignalCameraList();
        }

        return CommonResponse.ok(deviceInfoVos);
    }

    @Override
    public CommonResponse getOpcNodeNameList(String opcServerId) {
        List<OpcNodeInfo> opcNodeInfoList = list(new QueryWrapper<OpcNodeInfo>()
                .select("id", "communication_node_name").eq("opc_server_info_id", opcServerId));
        if (CollectionUtils.isEmpty(opcNodeInfoList)){
            return CommonResponse.ok();
        }else {
            List<OpcNodeInfo> sortedList = opcNodeInfoList.stream()
                    .sorted(Comparator.comparing(OpcNodeInfo::getCommunicationNodeName)).collect(Collectors.toList());
            return CommonResponse.ok(sortedList);
        }
    }

    @Override
    public CommonResponse<String> delete(String opcDeviceId) {

        OpcNodeInfo opcNodeInfo = this.getOne(new QueryWrapper<OpcNodeInfo>().eq("id",opcDeviceId));
        OpcNodeInfoDto opcNodeInfoDto = new OpcNodeInfoDto();
        BeanUtils.copyProperties(opcNodeInfo,opcNodeInfoDto);

        disconnectOpcNode(opcNodeInfoDto,this);

        this.removeById(opcDeviceId);

        return CommonResponse.ok("节点删除成功");
    }



    @Override
    public CommonResponse<String> edit(OpcNodeInfoDto opcNodeInfoDto) {

        if (opcNodeInfoDto.isNeedReconnect()){
            disconnectOpcNode(opcNodeInfoDto,this);

            OpcNodeInfo opcNodeInfo = this.getById(opcNodeInfoDto.getId());
            BeanUtils.copyProperties(opcNodeInfoDto,opcNodeInfo);
            opcNodeInfo.setUpdateTime(LocalDateTime.now());
            opcNodeInfo.setUpdateUser(SecurityUtils.getLoginUserName());
            this.updateById(opcNodeInfo);

            OpcThreadPoolExecutor.executorService.submit(() -> {
                UaClient uaClient = OpcConstant.OPC_SERVER_ID_CLIENT_MAP.get(opcNodeInfoDto.getOpcServerInfoId());
                if (!ObjectUtil.isEmpty(uaClient)){
                    //开始进行设备节点的重连
                    try {
//                        UaSubscription uaSubscription = OpcConstant.OPC_CLIENT_SUBSCRIBE_MAP.get(opcNodeInfoDto.getOpcServerInfoId());
                        new DynamicOpcNodeSubscriber(uaClient,new OpcNodeInfoServiceImpl())
                                .addMonitoredItem(new NodeId(opcNodeInfo.getNamespace(),opcNodeInfo.getIdentifier()));
                    } catch (Exception e) {
                        log.error("设备节点连接失败",e);
                    }
                }
            });


        }else {
            //只编辑名称
            this.update(new UpdateWrapper<OpcNodeInfo>().eq("id",opcNodeInfoDto.getId()).set("communication_node_name",opcNodeInfoDto.getCommunicationNodeName()));
        }
        return CommonResponse.ok("修改成功");
    }

    @Override
    public IPage selectByPage(OpcNodeInfoPage page) {

        QueryWrapper<OpcNodeInfo> queryWrapper = page.getQuery(new QueryWrapper<OpcNodeInfo>());
        OpcNodeInfoPage nodeInfoPage = this.page(page, queryWrapper);

        return nodeInfoPage;
    }

    @Override
    public void nodeConnectOpcServer(String opcServerId) {

        //获取当前opc server下的所有节点，并尝试进行连接
        List<OpcNodeInfo> opcNodeInfoList = this.list(new QueryWrapper<OpcNodeInfo>().eq("opc_server_info_id", opcServerId));
        if (!CollectionUtils.isEmpty(opcNodeInfoList)){
            for (OpcNodeInfo opcNodeInfo : opcNodeInfoList) {
                OpcThreadPoolExecutor.executorService.submit(new OpcNodeRunnable(opcNodeInfo,iOpcServerInfoService,this));
            }
        }

    }

    @Override
    public void reSubscribeOpcNode(String opcServerId) {
        OpcServerInfo opcServerInfo = iOpcServerInfoService.getById(opcServerId);
        List<OpcNodeInfo> opcNodeInfoList = this.list(new QueryWrapper<OpcNodeInfo>().eq("opc_server_info_id", opcServerId));
        if (!CollectionUtils.isEmpty(opcNodeInfoList)){
            //缓存所有的node节点
            for (OpcNodeInfo opcNodeInfo : opcNodeInfoList) {
                stringRedisTemplate.opsForValue().set(RedisConstants.OPC_NODE_NODE_INFO+opcNodeInfo.getIdentifier(),opcNodeInfo.getId());
                OpcThreadPoolExecutor.executorService.submit(new OpcNodeRunnable(opcNodeInfo,iOpcServerInfoService,this));
            }
        }else {
            log.info(" opc设备: {} 下，无需要监控的节点 ",opcServerInfo.getOpcServerName());
        }

    }

    /**
     * 移除正在监听的opc节点
     * @param opcNodeInfoDto dto
     */
    private  void disconnectOpcNode(OpcNodeInfoDto opcNodeInfoDto,IOpcNodeInfoService iOpcNodeInfoService) {
        OpcThreadPoolExecutor.executorService.submit(new Runnable() {
            @Override
            public void run() {
                //移除正在监听的节点
                UaClient uaClient = OpcConstant.OPC_SERVER_ID_CLIENT_MAP.get(opcNodeInfoDto.getOpcServerInfoId());
                if (!ObjectUtil.isEmpty(uaClient)){
                    try {
                        // TODO: 2024-11-15 前提是opc连接已经建立
//                        UaSubscription uaSubscription = OpcConstant.OPC_CLIENT_SUBSCRIBE_MAP.get(opcNodeInfoDto.getOpcServerInfoId());
                        new DynamicOpcNodeSubscriber(uaClient,iOpcNodeInfoService)
                                .removeMonitoredItem(new NodeId(opcNodeInfoDto.getNamespace(), opcNodeInfoDto.getIdentifier()));
                    } catch (Exception e) {
                        log.error("移除监听节点出现异常：",e);
                    }
                }
            }
        });
    }

}
